package com.googlecode.groovy_toy_orm

import com.googlecode.groovy_toy_orm.internal.Util
import groovy.transform.ToString


/**
 * @author: DavydovMD
 * Date: 28.03.13
 * Time: 16:20
 */
@ToString()
public class MapperFields extends BuilderSupport {
    List<Field> fields = []

    MapperFields copy() {
        MapperFields builderCopy = new MapperFields()
        builderCopy.fields = []
        fields.each {
            builderCopy.fields << it.copy()
        }
        builderCopy
    }

    MapperFields copy(String prefix) {
        MapperFields builderCopy = new MapperFields()
        builderCopy.fields = []
        fields.each {
            builderCopy.fields << it.copy(prefix)
        }
        builderCopy
    }

    /**
     * Copy all fields from another MapperFields (retaining fields that already exists)
     * @param mb mapperBuilder to copy
     * @return this
     */

    public MapperFields add(MapperFields mb) {
        mb.getFields().each { Field inField ->
            if (!fields.grep { Field fld -> fld.name == inField.name }) {
                fields.add(inField.copy())
            }
        }
        this
    }

    /**
     * Add fields using closure
     * @param closure
     * @return
     */

    public MapperFields build(Closure closure) {
        this.with(closure)
        this
    }

    public void field(String name, Object from = [name], Closure transform = null, Closure transformBack = null) {
        Map params = [:].with {
            put("name", name)
            put("from", from)
            if (transform != null) put("transform", transform)
            if (transformBack != null) put("transformBack", transformBack)
            delegate as Map
        }

        field(new Field(params))
    }

    public void field(Map params) {
        fields << new Field(params)
    }

    public void field(Field field) {
        fields << field
    }

    @Override
    protected void setParent(Object parent, Object child) {
    }

    private static Object parseName(Object name) {
        ((String) name).replace("\$", "")
    }

    @Override
    protected Object createNode(Object name) {
        fields << new Field(from: [parseName(name)], name: parseName(name))
    }

    @Override
    protected Object createNode(Object name, Object value) {
        fields << new Field(from: (value instanceof List ? value : [value]), name: parseName(name))
    }

    @Override
    protected Object createNode(Object name, Map attributes) {
        def params = [name: parseName(name), *: attributes]
        fields << new Field(params)
    }

    @Override
    protected Object createNode(Object name, Map attributes, Object value) {
        if (value instanceof Closure) {
            def params = [transform: value as Closure, *: attributes]
            fields << createNode(parseName(name), params)
        } else {
            /* Ignore value parameter*/
            fields << createNode(parseName(name), attributes)
        }
    }

//    /**
//     * Support for fully qualified fields names
//     * */
//


    /**
     *
     * @param name
     * @return
     * @throws NoSuchElementException
     */
    public Field getField(String name) {
        //TODO: back!!!
        name = Field.simplifyName(name)

        try {
            if (Field.isSimpleName(name)){
                return fields.grep() {
                    Field f -> (f.simpleName) == name
                }.first()
            } else {
                return fields.grep() {
                    Field f -> name == f.name
                }.first()
            }
        }

        catch (NoSuchElementException ignored) {
            throw new NoSuchFieldException("No such field defined, ${name}")
        }
    }

    public List<String> mappedFields() {
        def List ret = new ArrayList<String>()
        fields.each {
            (it.from instanceof Collection) ?
                ret.addAll(it.from as Collection)
            : ret << it.from
        }
        ret
    }

}
